/*	--- 抽象工厂 --- 


# 提供一个【创建一系列相关/相互依赖对象的接口】，而【无需】指定它们【具体的类】。

==============================


【抽象工厂模式】（Abstract Factory）是一个比较复杂的【创建型模式】。

【抽象工厂模式】和【工厂方法】不太一样，它要解决的问题比较复杂，

不但【工厂】是抽象的，【产品】是抽象的，而且有【多个产品】需要创建，

因此，这个【抽象工厂】会对应到【多个实际工厂】，【每个实际工厂】负责创建【多个实际产品】：


	                                ┌────────┐
	                            |─ >│ProductA│
	┌────────┐    ┌─────────┐   │   └────────┘
	│ Client │─ ─>│ Factory │─ ─
	└────────┘    └─────────┘   │   ┌────────┐
	                   ▲         ─ >│ProductB│
	           ┌───────┴───────┐    └────────┘
	           │               │
	      ┌─────────┐     ┌─────────┐
工厂		  │Factory1 │     │Factory2 │
	      └─────────┘     └─────────┘
	           │   ┌─────────┐ │   ┌─────────┐
产品接口	        ─ >│ProductA1│  ─ >│ProductA2│
	           │   └─────────┘ │   └─────────┘
	      ---------------------------------------
	               ┌─────────┐     ┌─────────┐
	           └ ─>│ProductB1│ └ ─>│ProductB2│
	               └─────────┘     └─────────┘

	    ========================================
	    实际产品
	            ┌─────────┐     ┌─────────┐
	           	│ProductB1│     │ProductB2│
	            └─────────┘	    └─────────┘

AbstractFactory(Root) -> Factory(Which?) -> Product(Which?)


【这种模式】有点类似于【多个供应商】负责提供【一系列类型的产品】, 而你要做的类似是【所有供应商的对接入口】。


===============

我们举个例子：

假设我们希望为用户提供一个【Markdown文本】转换为【HTML和Word】的【服务】，它的接口定义如下： 

	A
	*/
public interface AbstractFactory { // Level_0_interface
	// 创建Html文档
	HtmlDocument createHtml(String md); 

	// 创建Word文档
	WordDocument createWord(String s);
}


/*
注意到【上面的抽象工厂】仅仅是一个【接口】，没有任何代码。

同样的，因为 HtmlDocument 和 WordDocument 都比较复杂， 

现在我们并不知道如何实现它们，所以只有接口：*/

// 产品接口
public interface HtmlDocument { // Level_1_interface
	String toHtml();

	void save(Path path) throws IOException;
}

public interface WordDocument { // Level_1_interface
	void save(Path path) throws IOException;
}


/*
这样，我们就定义好了【抽象工厂】（AbstractFactory）以及 2个【抽象产品】（HtmlDocument和WordDocument）。

因为实现它们比较困难，我们决定让【供应商】来完成。


======================


现在市场上有 2家供应商：【FastDoc Soft】的产品便宜，并且转换速度快，而【GoodDoc Soft】的产品贵，但转换效果好。

我们决定同时使用这【2家供应商的产品】，以便给免费用户和付费用户【提供不同的服务】。

我们先看看 【FastDoc Soft的产品】 是如何实现的。

首先，【FastDoc Soft】 必须要有【实际的产品】，即 FastHtmlDocument 和 FastWordDocument：*/

// 产品接口 -> 实际产品
public class FastHtmlDocument implements HtmlDocument { 
// HtmlDocument 是 产品接口
	public String toHtml() {
		// ...
	}

	public void save(Path path) throws IOException {
		// ...
	}
}

public class FastWordDocument implements WordDocument { 
// WordDocument 是 产品接口
	public String toWord() {
		// ...
	}

	public void save(Path path) throws IOException {
		// ... 
	}
}



/*
然后，FastDoc Soft 必须提供 一个【实际的工厂】 来 生产 这【2种产品】，

即 【FastFactory】： */

// 实际工厂(implements AbstractFactory)
public class FastFactory implements AbstractFactory {

	public HtmlDocument createHtml(String md) {
		return new FastHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new FastWordDocument(md);
	}
}

public class GoodFactory implements AbstractFactory {

	public HtmlDocument createHtml(String md) {
		return new GoodHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new GoodWordDocument(md);
	}
}


/*
这样，我们就可以使用【FastDoc Soft的服务】了。

客户端编写代码如下： */

// 创建AbstractFactory, 【实际类型】是【FastFactory】
AbstractFactory factory = new FastFactory();

// 生成Html文档
HtmlDocument html = factory.createHtml("#Hello\nHello, world!");
html.save(Paths.get(".", "fast.html"));

// 生成Word文档
WordDocument word = fastFactory.createWord("#Hello\nHello, world!");
word.save(Paths.get(".", "fast.doc"));



/*
如果我们要同时使用【GoodDoc Soft的服务】怎么办？

因为用了【抽象工厂模式】，【GoodDoc Soft】只需要根据我们定义的【抽象工厂和抽象产品接口】，实现【自己的实际工厂和实际产品】即可： */

// 实际工厂
public class GoodFactory implements AbstractFactory {
	public HtmlDocument createHtml(String md) {
		return new GoodHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new GoodWordDocument(md);
	}
}

// 实际产品
public class GoodHtmlDocument implements HtmlDocument {
	// ... 
}

public class GoodWordDocument implements HtmlDocument {
	// ...
}


/*
【客户端】要使用【GoodDoc Soft的服务】，只需要把 原来的new FastFactory()  切换为 new GoodFactory() 即可。

注意到【客户端代码】除了通过【new】创建了【FastFactory】或【GoodFactory】外，其余代码只引用了【产品接口】，【并未引用】任何【实际产品】


（例如，FastHtmlDocument）
如果把【创建工厂的代码】放到AbstractFactory中, 就可以连【实际工厂】也屏蔽了： */
public interface AbstractFactory {
	public static AbstractFactory createFactory(String name) {
		if (name.equalsIgnoreCase("fast")) {
			return new FastFactory();
		} else if (name.equalsIgnoreCase("good")) {
			return new GoodFactory();
		} else {
			throw new IllegalArgumentException("Invalid factory name");
		}
	}
}


/*
我们来看看FastFactory和GoodFactory创建的WordDocument的实际效果：


注意：出于简化代码的目的，我们只支持两种Markdown语法：以#开头的标题以及普通正文。



=========================================================


#	----- 抽象工厂 の 小结 ----- 


1. 【抽象工厂模式】是为了让【创建工厂和创建一组产品】与【工厂、产品の使用】相分离，并可以随时切换到【另一个工厂】以及【另一组产品】；

2. 【抽象工厂模式】实现的关键点是【定义工厂接口】和【产品接口】，但【如何实现工厂与产品本身】需要留给【具体的子类】实现，【客户端】只和【抽象工厂与抽象产品】打交道。





========================

总结-抽象工厂の思想:

2个Interface, 用于抽象:
	· AbstractFactory(工厂)
	· 产品类型

由2个Interface, implements Interface 出 class 实际工厂/产品
	
class 实际工厂, 包含了 【create实际产品】 的 Method, 并该Method, return 实际产品

class 实际产品, 包含了 【原材料String 转换 实际产品Html】 的方法

----

实际使用：

Server: 
	1. 新建【实际工厂】, 但用【抽象工厂】作为表面, 内容为【实际工厂】
	2. 【工厂】 调用【生产方法】生产出【实际产品】, 但使用【抽象产品类型】代替【实际产品】
	3. 【产品】调用【产品功能】

Client:
	1. 只关注【工厂接口】，【产品接口】

-------

模板： 

Server端： */
public interface AbstractFactory { 
	// 创建Html文档
	HtmlDocument createHtml(String md); 

	// 创建Word文档
	WordDocument createWord(String s);
}



// 实际工厂
public class FastFactory implements AbstractFactory {

	public HtmlDocument createHtml(String md) {
		return new FastHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new FastWordDocument(md);
	}
}

public class GoodFactory implements AbstractFactory {

	public HtmlDocument createHtml(String md) {
		return new GoodHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new GoodWordDocument(md);
	}
}



// 产品类型(接口 = 初态)
public interface HtmlDocument { // Level_1_interface
	String toHtml();

	void save(Path path) throws IOException;
}

public interface WordDocument { // Level_1_interface
	void save(Path path) throws IOException;
}



// 实际产品(implements 产品类型)
public class FastHtmlDocument implements HtmlDocument { 
// HtmlDocument 是 产品接口
	public String toHtml() {
		// ...
	}

	public void save(Path path) throws IOException {
		// ...
	}
}

public class FastWordDocument implements WordDocument { 
// WordDocument 是 产品接口
	public String toWord() {
		// ...
	}

	public void save(Path path) throws IOException {
		// ... 
	}
}



// Main
public class Main {
	public static void main(String[] args) {

		// 1.实际工厂
		AbstractFactory factory = new FastFactory();

		// 2.产品类型 = 实际工厂.create产品
		HtmlDocument html = factory.createHtml("#Hello\nHello, world!");
		html.save(Paths.get(".", "fast.html"));

		WordDocument word = fastFactory.createWord("#Hello\nHello, world!");
		word.save(Paths.get(".", "fast.doc"));
	}
}




// Good
public class GoodFactory implements AbstractFactory {
	public HtmlDocument createHtml(String md) {
		return new GoodHtmlDocument(md);
	}

	public WordDocument createWord(String md) {
		return new GoodWordDocument(md);
	}
}

// 实际产品
public class GoodHtmlDocument implements HtmlDocument {
	// ... 
}

public class GoodWordDocument implements HtmlDocument {
	// ...
}



/*-------------------

Client端: */
public interface AbstractFactory {
	public static AbstractFactory createFactory(String name) {
		if (name.equalsIgnoreCase("fast")) {
			return new FastFactory();
		} else if (name.equalsIgnoreCase("good")) {
			return new GoodFactory();
		} else {
			throw new IllegalArgumentException("Invalid factory name");
		}
	}
}




